﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Runtime.CompilerServices;

namespace _5E.Utils.CollectionHelper
{
    public class DictionaryCache : IGenericCache
    {
        private Dictionary<Type, IDictionary> m_cache = new Dictionary<Type, IDictionary>();

        [MethodImpl(MethodImplOptions.Synchronized)]
        public void Add<T>(string key, T value)
        {
            Dictionary<string, T> typeCache;
            if (!TryGetTypeCache<T>(out typeCache))
                m_cache.Add(typeof(T), typeCache);
            if (!typeCache.ContainsKey(key))
                typeCache.Add(key, value);
        }

        [MethodImpl(MethodImplOptions.Synchronized)]
        public void Remove<T>(string key)
        {
            Dictionary<string, T> typeCache;
            if (TryGetTypeCache<T>(out typeCache))
            {
                if (typeCache.ContainsKey(key))
                    typeCache.Remove(key);
                if (0 == typeCache.Count)
                    m_cache.Remove(typeof(T));
            }
        }

        [MethodImpl(MethodImplOptions.Synchronized)]
        public void Merge<T>(string key, T value)
        {
            if (ContainsKey<T>(key))
                m_cache[typeof(T)][key] = value;
            else
                Add(key, value);
        }

        public T GetValue<T>(string key)
        {
            Dictionary<string, T> typeCache;
            if (TryGetTypeCache<T>(out typeCache) && typeCache.ContainsKey(key))
                return typeCache[key];
            return default(T);
        }

        public bool TryGetValue<T>(string key, out T value)
        {
            Dictionary<string, T> typeCache;
            if (TryGetTypeCache<T>(out typeCache))
                return typeCache.TryGetValue(key, out value);
            value = default(T);
            return false;
        }

        public bool ContainsKey<T>(string key)
        {
            Dictionary<string, T> typeCache;
            return TryGetTypeCache<T>(out typeCache) && typeCache.ContainsKey(key);
        }

        public bool ContainsValue<T>(T value)
        {
            Dictionary<string, T> typeCache;
            return TryGetTypeCache<T>(out typeCache) && typeCache.ContainsValue(value);
        }

        private bool TryGetTypeCache<T>(out Dictionary<string, T> typeCache)
        {
            Type type = typeof(T);
            if (m_cache.ContainsKey(type))
            {
                typeCache = m_cache[type] as Dictionary<string, T>;
                return true;
            }
            typeCache = new Dictionary<string, T>();
            return false;
        }

        public void Clear()
        {
            m_cache.Clear();
        }
    }
}
